/*
 * Copyright (C) 2014 Lucid Wolf
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.lucidhex.io;

import com.lucidhex.LucidXMLObject;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Wolf
 */
public class LucidXMLObjectConverter  implements Converter{
    private Class<? extends LucidXMLObject> c = null;
    private XStream xStream = null;
    private final LucidWorldParser parser;

    public LucidXMLObjectConverter(LucidWorldParser parser, Class<? extends LucidXMLObject> c) {
        this.parser = parser;
        this.c = c;
        this.xStream = parser.getXstream();
    }

    @Override
    public void marshal(Object o, HierarchicalStreamWriter writer, MarshallingContext mc) {
        // write to XML
        LucidXMLObject out = (LucidXMLObject)o;  // really should equal this not going to check
        
        TreeMap<String,String> attrib = new TreeMap<>();
        out.getFields(attrib);
        for(String key : attrib.keySet()){
            writer.addAttribute(key, attrib.get(key));
        }
        ArrayList<Object> children = new ArrayList<>();
        out.getChildren(children);
        for(Object kid : children){
            boolean writeOut = true;
            if(kid instanceof LucidXMLObject){
                writeOut = ((LucidXMLObject)kid).writeToXml();
            }
            String kidName = xStream.getMapper().serializedClass(kid.getClass());
            if(kidName != null && writeOut){
                writer.startNode(kidName);
                mc.convertAnother(kid);
                writer.endNode();
            }
        }      
    }

    @Override
    public LucidXMLObject unmarshal(HierarchicalStreamReader reader, UnmarshallingContext uc) {
        LucidXMLObject obj = null;
        try {
            obj = c.newInstance();
        } catch (InstantiationException | IllegalAccessException ex) {
            Logger.getLogger(LucidXMLObject.class.getName()).log(Level.SEVERE, null, ex);
        }
        if(obj == null){return null;}
        // link stuff if needed
        obj.parseLink(parser);
        Iterator<String> iter = reader.getAttributeNames();
        while(iter.hasNext()){
            String id = iter.next();
            obj.addField(id, reader.getAttribute(id));
        }
        while(reader.hasMoreChildren()){
            reader.moveDown();
            String aType = reader.getNodeName().trim();
            // check attributes ?
            
            // scan for type and load it
            Class ac = xStream.getMapper().realClass(aType);
            if(ac != null){
                Object aChild = uc.convertAnother(obj, ac);
                obj.addChild(aChild);
            }
            reader.moveUp();
            
        }
        return obj;
    }

    public boolean isClassRegistered(){
        return c != null;
    }
    @Override
    public boolean canConvert(Class type) {
        // should i test for null maybe else a null type will be registered
        return type.equals(c);
    }


}
